资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。
在 webpack 5 之前,通常使用:
raw-loader 将文件导入为字符串url-loader 将文件作为 data URI 内联到 bundle 中file-loader 将文件发送到输出目录资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。当在 webpack 5 中使用旧的 assets loader(如 file-loader/url-loader/raw-loader 等)和 asset 模块时,你可能想停止当前 asset 模块的处理,并再次启动处理,这可能会导致 asset 重复,你可以通过将 asset 模块的类型设置为 'javascript/auto' 来解决。
webpack.config.js
module.exports = { module: {rules: [ {test: /\.(png|jpg|gif)$/i,use: [ {loader: 'url-loader',options: { limit: 8192,} },],+type: 'javascript/auto' },] },}如需从 asset loader 中排除来自新 URL 处理的 asset,请添加 dependency: { not: ['url'] } 到 loader 配置中。
webpack.config.js
module.exports = { module: {rules: [ {test: /\.(png|jpg|gif)$/i,+dependency: { not: ['url'] },use: [ {loader: 'url-loader',options: { limit: 8192,}, },], },], }}Resource 资源webpack.config.js
const path = require('path');module.exports = { entry: './src/index.js', output: {filename: 'main.js',path: path.resolve(__dirname, 'dist') },+ module: {+rules: [+ {+test: /\.png/,+type: 'asset/resource'+ }+]+ },};src/index.js
import mainImage from './images/main.png';img.src = mainImage; // '/dist/151cfcfa1bd74779aadb.png'所有 .png 文件都将被发送到输出目录,并且其路径将被注入到 bundle 中,除此之外,你可以为它们自定义 outputPath 和 publicPath 属性。
自定义输出文件名默认情况下,asset/resource 模块以 [hash][ext][query] 文件名发送到输出目录。
可以通过在 webpack 配置中设置 output.assetModuleFilename 来修改此模板字符串:
webpack.config.js
const path = require('path');module.exports = { entry: './src/index.js', output: {filename: 'main.js',path: path.resolve(__dirname, 'dist'),+assetModuleFilename: 'images/[hash][ext][query]' }, module: {rules: [ {test: /\.png/,type: 'asset/resource' }] },};另一种自定义输出文件名的方式是,将某些资源发送到指定目录:
const path = require('path');module.exports = { entry: './src/index.js', output: {filename: 'main.js',path: path.resolve(__dirname, 'dist'),+assetModuleFilename: 'images/[hash][ext][query]' }, module: {rules: [ {test: /\.png/,type: 'asset/resource'- }+ },+ {+test: /\.html/,+type: 'asset/resource',+generator: {+ filename: 'static/[hash][ext][query]'+}+ }] },};使用此配置,所有 html 文件都将被发送到输出目录中的 static 目录中。
Rule.generator.filename 与 output.assetModuleFilename 相同,并且仅适用于 asset 和 asset/resource 模块类型。
inline 资源(inlining asset)webpack.config.js
const path = require('path');module.exports = { entry: './src/index.js', output: {filename: 'main.js',path: path.resolve(__dirname, 'dist'),-assetModuleFilename: 'images/[hash][ext][query]' }, module: {rules: [ {-test: /\.png/,-type: 'asset/resource'+test: /\.svg/,+type: 'asset/inline'- },+ }- {-test: /\.html/,-type: 'asset/resource',-generator: {- filename: 'static/[hash][ext][query]'-}- }] }};src/index.js
- import mainImage from './images/main.png';+ import metroMap from './images/metro.svg';- img.src = mainImage; // '/dist/151cfcfa1bd74779aadb.png'+ block.style.background = `url(${metroMap})`; // url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDo...vc3ZnPgo=)所有 .svg 文件都将作为 data URI 注入到 bundle 中。
自定义 data URI 生成器webpack 输出的 data URI,默认是呈现为使用 Base64 算法编码的文件内容。
如果要使用自定义编码算法,则可以指定一个自定义函数来编码文件内容:
webpack.config.js
const path = require('path');+ const svgToMiniDataURI = require('mini-svg-data-uri');module.exports = { entry: './src/index.js', output: {filename: 'main.js',path: path.resolve(__dirname, 'dist') }, module: {rules: [ {test: /\.svg/,type: 'asset/inline',+generator: {+ dataUrl: content => {+content = content.toString();+return svgToMiniDataURI(content);+ }+} }] },};现在,所有 .svg 文件都将通过 mini-svg-data-uri 包进行编码。
source 资源(source asset)webpack.config.js
const path = require('path');- const svgToMiniDataURI = require('mini-svg-data-uri');module.exports = { entry: './src/index.js', output: {filename: 'main.js',path: path.resolve(__dirname, 'dist') }, module: {rules: [ {-test: /\.svg/,-type: 'asset/inline',-generator: {- dataUrl: content => {-content = content.toString();-return svgToMiniDataURI(content);- }-}+test: /\.txt/,+type: 'asset/source', }] },};src/example.txt
Hello worldsrc/index.js
- import metroMap from './images/metro.svg';+ import exampleText from './example.txt';- block.style.background = `url(${metroMap}); // url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDo...vc3ZnPgo=)+ block.textContent = exampleText; // 'Hello world'所有 .txt 文件将原样注入到 bundle 中。
URL 资源当使用 new URL('./path/to/asset', import.meta.url),webpack 也会创建资源模块。
src/index.js
const logo = new URL('./logo.svg', import.meta.url);根据你配置中 target 的不同,webpack 会将上述代码编译成不同结果:
// target: webnew URL( __webpack_public_path__ + 'logo.svg', document.baseURI || self.location.href);// target: webworkernew URL(__webpack_public_path__ + 'logo.svg', self.location);// target: node, node-webkit, nwjs, electron-main, electron-renderer, electron-preload, async-nodenew URL( __webpack_public_path__ + 'logo.svg', require('url').pathToFileUrl(__filename));自 webpack 5.38.0 起,Data URLs 也支持在 new URL() 中使用了:
src/index.js
const url = new URL('data:,', import.meta.url);console.log(url.href === 'data:,');console.log(url.protocol === 'data:');console.log(url.pathname === ',');通用资源类型webpack.config.js
const path = require('path');module.exports = { entry: './src/index.js', output: {filename: 'main.js',path: path.resolve(__dirname, 'dist') }, module: {rules: [ {+test: /\.txt/,+type: 'asset', }] },};现在,webpack 将按照默认条件,自动地在 resource 和 inline 之间进行选择:小于 8kb 的文件,将会视为 inline 模块类型,否则会被视为 resource 模块类型。
可以通过在 webpack 配置的 module rule 层级中,设置 Rule.parser.dataUrlCondition.maxSize 选项来修改此条件:
webpack.config.js
const path = require('path');module.exports = { entry: './src/index.js', output: {filename: 'main.js',path: path.resolve(__dirname, 'dist') }, module: {rules: [ {test: /\.txt/,type: 'asset',+parser: {+ dataUrlCondition: {+maxSize: 4 * 1024 // 4kb+ }+} }] },};还可以 指定一个函数 来决定是否 inline 模块。
变更内联 loader 的语法在 asset 模块和 webpack 5 之前,可以使用内联语法与上述传统的 loader 结合使用。
现在建议去掉所有的内联 loader 的语法,使用资源查询条件来模仿内联语法的功能。
示例,将 raw-loader 替换为 asset/source 类型:
- import myModule from 'raw-loader!my-module';+ import myModule from 'my-module?raw';webpack 相关配置:
module: {rules: [// ...+ {+resourceQuery: /raw/,+type: 'asset/source',+ }] },如果你想把原始资源排除在其他 loader 的处理范围以外,请使用使用取反的正则:
module: {rules: [// ...+ {+test: /\.m?js$/,+resourceQuery: { not: [/raw/] },+use: [ ... ]+ }, {resourceQuery: /raw/,type: 'asset/source', }] },或者使用 oneOf 的规则列表。此处只应用第一个匹配规则:
module: {rules: [// ...+ { oneOf: [{ resourceQuery: /raw/, type: 'asset/source',},+{+ test: /\.m?js$/,+ use: [ ... ]+},+ ] }] },禁止生成资源对于像服务器端渲染这样的用例,若是希望禁止生成资源,可以通过在 Rule.generator 下使用 emit 选项来实现。
module.exports = { // … module: {rules: [ {test: /\.png$/i,type: 'asset/resource',generator: { emit: false,}, },], },};Further Readingwebpack 5 - 资源模块